#ifndef PLEX_MEMORY_H
#define PLEX_MEMORY_H

/**
 * @file plex.h
 * @brief plex memory operation
 */

#include <stdlib.h>

#ifdef __cplusplus
extern "C" {
#endif

/* memory block */
struct mem_block
{
    struct mem_block* next;
    char data[0];
};

typedef struct plex plex_t;

struct plex
{
    struct mem_block* head;
    struct mem_block* free;
    unsigned int size;
    unsigned int nmemb;
#ifdef PLEX_DEBUG
    unsigned int count_alloc;
    unsigned int count_free;
#endif
};

static inline void plex_init(struct plex* me, unsigned int nmemb, unsigned int size)
{
    me->head = 0;
    me->free = 0;
    me->size = size<sizeof(void*) ? sizeof(void*) : size;
    me->nmemb = nmemb<0 ? 1 : nmemb;
#ifdef PLEX_DEBUG
    me->count_alloc = 0;
    me->count_free = 0;
#endif
}

static inline void plex_free(struct plex* me, void* ptr)
{
    ((struct mem_block*)ptr)->next = me->free;
    me->free = ptr;
#ifdef PLEX_DEBUG
    me->count_free++;
#endif
}

static inline void* plex_alloc(struct plex* me)
{
    void* ret = 0;

    if (!me->free)
    {
        struct mem_block* block = malloc(sizeof(struct mem_block) + me->nmemb * me->size);
        char* data = 0;
        unsigned int i = 0;

        if (!block)
            return 0;
        //join to head
        block->next = me->head;
        me->head = block;

        //join to free_list
        data = block->data;
        for (; i<me->nmemb; i++)
        {
            plex_free(me, data);
            data += me->size;
#ifdef PLEX_DEBUG
            me->count_free--;//becouse plex_free() exec count_free++
#endif            
        }
    }

    ret = me->free;
    me->free = me->free->next;
#ifdef PLEX_DEBUG
    me->count_alloc++;
#endif
    return ret;
}

static inline void plex_clear(struct plex* me)
{
    while(me->head)
    {
        struct mem_block* bak = me->head;
        me->head = bak->next;
        free(bak);
    }
    me->head = 0;
    me->free = 0;
#ifdef PLEX_DEBUG
    me->count_alloc = 0;
    me->count_free = 0;
#endif
}

static inline struct plex* plex_new(unsigned int nmemb, unsigned int size)
{
    struct plex* me = malloc(sizeof(struct plex));
    if (me)
        plex_init(me, nmemb, size);
    return me;
}

static inline void plex_delete(struct plex* me)
{
    plex_clear(me);
    free(me);
}

#define ITEM_MALLOC(plex, size) \
    (plex)? plex_alloc(plex) : malloc(size); \

#define ITEM_FREE(plex, ptr) \
    (plex)? plex_free(plex, ptr) : free(ptr); \
    


#ifdef __cplusplus
}
#endif

#endif /* PLEX_MEMORY_H */

